home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Source Code
/
Pascal
/
Code Resources
/
Eclectic CDEFs
/
Celsius CDEF Folder
/
CelsiusCDEF.p
< prev
next >
Wrap
Text File
|
1997-03-05
|
30KB
|
770 lines
{ CelsiusCDEF }
{}
{ Control definition for the CelsiusCDEF control }
{}
{ Copyright © Sebastiano Pilla 1996 }
{ <mailto:case@tvol.it> }
{ Control definition procedure for implementing a progress bar similar to that used by the Finder. }
{ Additionally, by setting the appropriate variation codes, it is possible to: }
{ 1) obtain a 'barber pole' effect (indefinite progress bar) }
{ 2) ignore the colors in any control color table (either default or custom) and use always the standard colors }
{ 3) never dim the control, thus drawing always in the 'active' state }
{ 4) draw a 3D-like inset effect }
{ Credits }
{}
{ Chris Larson for FinderProgressBar }
{ Andrew Regan for ProgressBar CDEF }
{ Jim Stout for Jim's CDEF }
{ Harold Ekstrom for Slider CDEF }
unit CelsiusCDEF;
interface
uses
Windows, Palettes, CDEFUtils, CalculateBarBoundaryIntf;
function Main (inVarCode: SInt16;
inControlHdl: ControlHandle;
inMessage: ControlDefProcMessage;
inParam: SInt32): SInt32;
implementation
const
kInCelsiusControlPart = 50; { Value returned for the testCntl message }
kBarberPoleVarCodeMask = $1; { Mask for obtaining the barber pole animation }
kUseStdColorsVarCodeMask = $2; { Mask for drawing the bar with the standard dark gray-steel blue colors }
kNeverDimControlVarCodeMask = $4; { Mask for drawing the bar always in the active state }
kInsetBorderVarCodeMask = $8; { Mask for drawing an inset border around the bar }
kDeviceLoopFlags = 0; { Flags passed to DeviceLoop }
kMinimumColorDepth = 4; { Minimum depth for drawing in color, in bits per pixel }
kMinimum3DDepth = 8; { Minimum depth for drawing the inset border, in bits per pixel }
kBarberStripeWidth = 8; { Width of barber pole strips }
kFPBFrameCount = 16; { Number of frames in the barber pole animation loop }
type
CelsiusCDEFDataHandle = ^CelsiusCDEFDataPtr;
CelsiusCDEFDataPtr = ^CelsiusCDEFData;
CelsiusCDEFData = record
fDrawControlUPP: DeviceLoopDrawingUPP; { Pointer to drawing routine }
fBlitControlUPP: DeviceLoopDrawingUPP; { Pointer to blitting routine }
fOffscreenWorldPtr: GWorldPtr; { Pointer to offscreen world }
fBlackPattern: Pattern; { Standard black pattern }
fWhitePattern: Pattern; { Standard white pattern }
fDitherPattern: Pattern; { 50% black-50% white pattern used for dimming }
fControlOwnerForeColor: RGBColor; { Foreground color of the control's window }
fControlOwnerContentColor: RGBColor; { Content color of the control's window }
fBlackColor: RGBColor; { ($0000, $0000, $0000) black color }
fWhiteColor: RGBColor; { ($FFFF, $FFFF, $FFFF) white color }
fDarkGrayColor: RGBColor; { ($4000, $4000, $4000) dark gray ('done' part) color }
fSteelBlueColor: RGBColor; { ($CCCC, $CCCC, $FFFF) steel blue ('to do' part) color }
fDimGrayColor: RGBColor; { ($7FFF, $7FFF, $7FFF) gray color for dimming }
fChiselGrayColor: RGBColor; { ($AAAA, $AAAA, $AAAA) chisel color as per develop 15 }
fVariationCode: SInt16; { VarCode of current control (no other way of passing it to the draw routine) }
fDepth: UInt16; { Saved depth state, for updating the offscreen world }
fOffscreenDrawAvailable: Boolean; { True if we can draw in the offscreen world, false otherwise }
end;
{ DrawInsetBorder }
{}
{ Draws a 3D-like inset border in the specified rectangle if the content color of the control's window }
{ is the standard light gray ($EEEE, $EEEE, $EEEE), as defined in develop 15 }
{}
{ Entry: inControlBounds = rectangle enclosing the control }
{ inWinBackColor = content color of the control's window }
{ inControlDataHdl = handle to border colors }
procedure Draw3DInsetEffect (inControlBounds: Rect;
inWinBackColor: RGBColor;
inControlDataHdl: CelsiusCDEFDataHandle);
begin
{ Draw the 3D border only if the window's content color is ($EEEE, $EEEE, $EEEE) }
if EqualRGBColorComponents(inWinBackColor, kLightGrayRGBComp) then
begin
RGBBackColor(inWinBackColor);
{ Draw the top and left margins with the gray color and the bottom and right margins with the white }
{ color to achieve an 'inset' effect }
RGBForeColor(inControlDataHdl^^.fWhiteColor);
MoveTo(inControlBounds.left + 1, inControlBounds.bottom - 1);
LineTo(inControlBounds.right - 1, inControlBounds.bottom - 1);
LineTo(inControlBounds.right - 1, inControlBounds.top);
RGBForeColor(inControlDataHdl^^.fChiselGrayColor);
MoveTo(inControlBounds.left, inControlBounds.bottom - 1);
LineTo(inControlBounds.left, inControlBounds.top);
LineTo(inControlBounds.right - 1, inControlBounds.top);
end;
end;
{ DrawBarberPoleBar }
{}
{ Draws the 'barber pole' animation effect }
{}
{ Entry: inControlBounds = rectangle for drawing (prepared by the caller) }
{ inDarkColor = darker color for the diagonal stripes }
{ inLightColor = lighter color for the diagonal stripes }
{ inWinBackColor = background color of the control's window }
{ inControlHdl = handle to current control, to get the value }
{ inDepth = depth of current device }
{ inDitherContentFlag = TRUE if the bar content should be dithered, false otherwise }
procedure DrawBarberPoleBar (inControlBounds: Rect;
inDarkColor, inLightColor, inWinBackColor: RGBColor;
inControlHdl: ControlHandle;
inDepth: UInt16;
inDitherContentFlag: Boolean);
var
i, height, frameNum: SInt16;
begin
{ Set the pen size for the diagonal stripes }
PenSize(kBarberStripeWidth, 1);
{ Setup the colors for drawing }
if inDepth >= kMinimumColorDepth then
begin
RGBForeColor(inDarkColor);
RGBBackColor(inLightColor);
end
else
begin
ForeColor(blackColor);
BackColor(whiteColor);
end;
{ Compute the horizontal starting point for drawing. Note that each drawing loop draws one }
{ stripe of each color (with gray drawn first). This starting point is set far enough to the }
{ left to make the first set of stripes drawn hit the lower left corner of the bar }
height := inControlBounds.bottom - inControlBounds.top;
frameNum := inControlHdl^^.contrlValue mod kFPBFrameCount;
i := height + frameNum + (2 * kBarberStripeWidth) - 2;
i := i div (2 * kBarberStripeWidth);
i := inControlBounds.left - (i * 2 * kBarberStripeWidth) + frameNum;
{ Now back bias the starting location to account for the first increment }
i := i - kBarberStripeWidth;
{ Until we would start drawing to the right of the bar, draw a pair of lines, slanting to the }
{ right, first of the two in the darker color, second in the lighter color }
while i < inControlBounds.right do
begin
i := i + kBarberStripeWidth;
PenMode(patCopy);
MoveTo(i, inControlBounds.top);
LineTo((i + height), (inControlBounds.bottom - 1));
i := i + kBarberStripeWidth;
PenMode(patBic);
MoveTo(i, inControlBounds.top);
LineTo((i + height), (inControlBounds.bottom - 1));
end;
{ If this control should be dimmed, paint the control's rectangle with the dithering pattern }
if inDitherContentFlag then
begin
if inDepth >= kMinimumColorDepth then
RGBBackColor(inWinBackColor);
PenMode(notPatBic);
PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fDitherPattern);
PaintRect(inControlBounds);
end;
end;
{ DrawNormalBar }
{}
{ Draws the normal done-to do progress bar using the supplied colors }
{}
{ Entry: inControlBounds = rectangle for drawing (prepared by the caller) }
{ inDarkColor = darker color for the 'done' part }
{ inLightColor = lighter color for the 'to do' }
{ inWinBackColor = background color of the control's window }
{ inControlHdl = handle to current control, to get the value }
{ inDepth = depth of current device }
{ inDitherContentFlag = TRUE if the bar content should be dithered, false otherwise }
procedure DrawNormalBar (inControlBounds: Rect;
inDarkColor, inLightColor, inWinBackColor: RGBColor;
inControlHdl: ControlHandle;
inDepth: UInt16;
inDitherContentFlag: Boolean);
var
savedBounds: Rect;
savedRightBound: SInt16;
begin
{ To properly dither the bar later, we must save here the current control's bounds, because }
{ they're altered by the following calls }
if inDitherContentFlag then
savedBounds := inControlBounds;
{ Save the current right border of the control's rect, because is modified by the CalculateBarBoundary call }
savedRightBound := inControlBounds.right;
inControlBounds.right := CalculateBarBoundary(inControlHdl, inControlBounds.left, inControlBounds.right);
{ Set up colors or pen pattern for the bar, then draw the bar }
if inDepth >= kMinimumColorDepth then
begin
RGBForeColor(inDarkColor);
RGBBackColor(inWinBackColor);
end
else
PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fBlackPattern);
PaintRect(inControlBounds);
{ Now set up the rectangle to draw the 'empty' space not yet filled by the bar }
inControlBounds.left := inControlBounds.right;
inControlBounds.right := savedRightBound;
{ Set up colors or pen pattern for the 'empty' space, then draw the space }
if inDepth >= kMinimumColorDepth then
begin
RGBForeColor(inLightColor);
RGBBackColor(inWinBackColor);
end
else
PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fWhitePattern);
PaintRect(inControlBounds);
{ Dither the content if we're asked, using the previously saved rect }
if inDitherContentFlag then
begin
if inDepth >= kMinimumColorDepth then
RGBBackColor(inWinBackColor);
PenMode(notPatBic);
PenPat(CelsiusCDEFDataHandle(inControlHdl^^.contrlData)^^.fDitherPattern);
PaintRect(savedBounds);
end;
end;
{ DrawCelsiusControl }
{}
{ DeviceLoop draw routine to draw the control in either the offscreen world or the control's port }
{}
{ Entry: inDepth = depth of current device }
{ inDeviceFlags = flags describing current device properties (unused) }
{ inTargetDevice = handle to current device }
{ inUserData = container for the control's handle }
procedure DrawCelsiusControl (inDepth: UInt16;
inDeviceFlags: SInt16;
inTargetDevice: GDHandle;
inUserData: SInt32);
var
controlBounds: Rect;
frameColor, darkColor, lightColor, winBackColor: RGBColor;
controlHdl: ControlHandle;
controlDataHdl: CelsiusCDEFDataHandle;
auxCtlHdl: AuxCtlHandle;
colorTabHdl: CCTabHandle;
varCode: SInt16;
hiliteState: UInt8;
useStdColorsFlag, dimFlag: Boolean;
begin
{ Get the control handle, the data handle and the control's hilite state }
controlHdl := ControlHandle(inUserData);
controlDataHdl := CelsiusCDEFDataHandle(controlHdl^^.contrlData);
hiliteState := controlHdl^^.contrlHilite;
{ Get the variation code and the control's rect (in local coordinates relative to the control's window) }
varCode := controlDataHdl^^.fVariationCode;
controlBounds := controlHdl^^.contrlRect;
{ Set the drawing port to either the offscreen world, if available, or the control's port }
if controlDataHdl^^.fOffscreenDrawAvailable then
SetGWorld(controlDataHdl^^.fOffscreenWorldPtr, nil)
else
SetGWorld(CGrafPtr(controlHdl^^.contrlOwner), nil);
{ Always normalize the pen before drawing, to avoid unwanted side effects }
PenNormal;
{ Dim the control only if the 'never dim' varCode is clear and if the control is inactive }
dimFlag := (BAND(varCode, kNeverDimControlVarCodeMask) = 0) & (hiliteState = kControlInactivePart);
{ Must we use the standard dark gray / steel blue colors? }
useStdColorsFlag := BAND(varCode, kUseStdColorsVarCodeMask) <> 0;
{ Set a well-known clipping; again, this helps avoiding surprises }
ClipRect(controlBounds);
{ Check if the current device is deep enough for drawing our colors; if so, either fetch the colors from the control's }
{ data if we have to use the standard colors, or get them from the auxiliary control record. }
{ Then draw the inset border (if requested) and the frame }
if inDepth >= kMinimumColorDepth then
begin
{ Fetch the content color of the control's window that the BeginDraw routine stored for us }
winBackColor := controlDataHdl^^.fControlOwnerContentColor;
{ Fetch the standard colors, stored at the control initialization in the control's data; also, pay attention to the }
{ control's hilite state and fetch the correct frame color }
if useStdColorsFlag then
begin
if dimFlag then
frameColor := controlDataHdl^^.fDimGrayColor
else
frameColor := controlDataHdl^^.fBlackColor;
darkColor := controlDataHdl^^.fDarkGrayColor;
lightColor := controlDataHdl^^.fSteelBlueColor;
end
else
begin
{ Retrieve the control's auxiliary record; this strange 'if ... then ;' statement discards the function result (we }
{ don't care about it) }
if GetAuxiliaryControlRecord(controlHdl, auxCtlHdl) then
;
{ Get the color table and lock it (necessary to avoid NIL dereferences) }
colorTabHdl := auxCtlHdl^^.acCTable;
HLock(Handle(colorTabHdl));
{ The control should be dimmed, so fetch the foreground color of the control's owner from the control's data and }
{ call GetGray to obtain the best gray for this device; note that if GetGray fails we use our standard dimGray color }
if dimFlag then
begin
frameColor := controlDataHdl^^.fControlOwnerForeColor;
if not GetGray(inTargetDevice, winBackColor, frameColor) then
frameColor := controlDataHdl^^.fDimGrayColor;
end
else
{ The control shouldn't be dimmed, so fetch the frame color from the associated color table }
frameColor := colorTabHdl^^.ctTable[cFrameColor].rgb;
{ The lighter color is the body color, and the darker color is the text color in the control's color table }
lightColor := colorTabHdl^^.ctTable[cBodyColor].rgb;
darkColor := colorTabHdl^^.ctTable[cTextColor].rgb;
{ Unlock the color table since we're done with it }
HUnlock(Handle(colorTabHdl));
end;
{ Determine if the control can feature the 'inset' effect, and call Draw3DInsetEffect }
if (inDepth >= kMinimum3DDepth) and (BAND(varCode, kInsetBorderVarCodeMask) <> 0) and (not dimFlag) then
Draw3DInsetEffect(controlBounds, winBackColor, controlDataHdl)
else
{ The 'inset' effect cannot be displayed, so frame the control's rectangle with the content color of the control's }
{ window. This maintains consistency: the rectangle in which we draw the actual progress bar (or barber }
{ pole) should always keep the same size, regardless of the presence of the inset border. Letting the bar height }
{ float causes very unpleasant effects if the user changes the screen depth between 2 subsequent drawings. }
begin
RGBForeColor(winBackColor);
RGBBackColor(winBackColor);
FrameRect(controlBounds);
end;
end
else
{ Black-&-white (or 4-colors) device: frame the control's rectangle with a white pattern. }
begin
PenPat(controlDataHdl^^.fWhitePattern);
FrameRect(controlBounds);
end;
{ Inset the control rect by 1 pixel to avoid overwriting the inset border, and for consistency in the case }
{ where the border is absent. See above for a more detailed explanation. }
InsetRect(controlBounds, 1, 1);
{ Draw the control's frame. }
if inDepth >= kMinimumColorDepth then
begin
RGBForeColor(frameColor);
RGBBackColor(winBackColor);
end
else if inDepth < kMinimumColorDepth then
if dimFlag then
PenPat(controlDataHdl^^.fDitherPattern)
else
PenPat(controlDataHdl^^.fBlackPattern);
FrameRect(controlBounds);
{ Inset the control rect by 1 pixel to avoid drawing over the frame; for maximum security, clip everything }
{ outside this newly inset rectangle }
InsetRect(controlBounds, 1, 1);
ClipRect(controlBounds);
{ Determine if we must draw the 'barber pole' animation or the regular bar and draw }
if BAND(varCode, kBarberPoleVarCodeMask) <> 0 then
DrawBarberPoleBar(controlBounds, darkColor, lightColor, winBackColor, controlHdl, inDepth, dimFlag)
else
DrawNormalBar(controlBounds, darkColor, lightColor, winBackColor, controlHdl, inDepth, dimFlag);
end;
{ BlitCelsiusControl }
{}
{ DeviceLoop draw routine to copy the control drawing from the offscreen world to the control's port }
{}
{ Entry: inDepth = depth of current device }
{ inDeviceFlags = flags describing current device properties (unused) }
{ inTargetDevice = handle to current device (unused) }
{ inUserData = container for the control's handle }
procedure BlitCelsiusControl (inDepth: UInt16;
inDeviceFlags: SInt16;
inTargetDevice: GDHandle;
inUserData: SInt32);
var
controlBounds: Rect;
controlDataHdl: CelsiusCDEFDataHandle;
offWorldPtr: GWorldPtr;
controlPort: CGrafPtr;
begin
{ Get the control's data, the control's port and the control's rect }
controlDataHdl := CelsiusCDEFDataHandle(ControlHandle(inUserData)^^.contrlData);
controlPort := CGrafPtr(ControlHandle(inUserData)^^.contrlOwner);
controlBounds := ControlHandle(inUserData)^^.contrlRect;
{ Proceed only if the offscreen world is available }
if controlDataHdl^^.fOffscreenDrawAvailable then
begin
offWorldPtr := controlDataHdl^^.fOffscreenWorldPtr;
{ The offscreen world's pixMap has already been locked by the caller, so we set the port }
{ to the control's owner, set the foreground color to black and background color to white to avoid colorization }
{ by CopyBits and start blitting }
SetGWorld(controlPort, nil);
RGBForeColor(controlDataHdl^^.fBlackColor);
RGBBackColor(controlDataHdl^^.fWhiteColor);
CopyBits(GrafPtr(offWorldPtr)^.portBits, GrafPtr(controlPort)^.portBits, offWorldPtr^.portRect, controlBounds, srcCopy, nil);
end;
end;
{ BeginDraw }
{}
{ Responds to the drawCntl message by saving the current port, color, ecc. settings, by activating ours and }
{ by calling DeviceLoop to draw the control }
{}
{ Entry: inControlHdl = handle to current control }
{ inVarCode = variation code of current control }
procedure BeginDraw (inControlHdl: ControlHandle;
inVarCode: SInt16);
var
controlBounds: Rect;
saveForeColor, saveBackColor: RGBColor;
savePen: PenState;
saveClip, controlRgn, drawingRgn: RgnHandle;
auxWinHdl: AuxWinHandle;
colorTabHdl: CTabHandle;
savePort, controlPort: CGrafPtr;
saveDevice: GDHandle;
controlDataHdl: CelsiusCDEFDataHandle;
ignoredFlags: GWorldFlags;
currDepth: UInt16;
offscreenDrawFlag: Boolean;
begin
{ Exit immediately if our custom data isn't available }
controlDataHdl := CelsiusCDEFDataHandle(inControlHdl^^.contrlData);
if controlDataHdl = nil then
Exit(BeginDraw);
{ Save current settings (port, clipping, colors, ecc.) }
GetGWorld(savePort, saveDevice);
controlPort := CGrafPtr(inControlHdl^^.contrlOwner);
SetGWorld(controlPort, nil);
GetForeColor(saveForeColor);
GetBackColor(saveBackColor);
GetPenState(savePen);
{ Now the current port is the control's port, and we intersect its clipping region with our control rectangle; if this }
{ intersection is empty then exit without drawing anything. This indeed means that all our drawing would be clipped out }
{ Get the visRgn of the control's window, to pass it to DeviceLoop later }
drawingRgn := controlPort^.visRgn;
controlBounds := inControlHdl^^.contrlRect;
{ Allocate 2 regions for our purposes }
controlRgn := NewRgn;
saveClip := NewRgn;
if (saveClip <> nil) and (controlRgn <> nil) then
begin
GetClip(saveClip);
RectRgn(controlRgn, controlBounds);
{ Intersect the current clip region with the control's rectangle: if this intersection is empty then dispose of our }
{ regions and exit }
SectRgn(saveClip, controlRgn, controlRgn);
if EmptyRgn(controlRgn) then
begin
DisposeRgn(saveClip);
DisposeRgn(controlRgn);
SetGWorld(savePort, saveDevice);
Exit(BeginDraw);
end
else
{ All right: set the clip region to the previously calculated intersection and go ahead }
SetClip(controlRgn);
end
else
{ Bad, bad: we don't have enough memory to allocate 2 regions, so exit }
begin
SetGWorld(savePort, saveDevice);
Exit(BeginDraw);
end;
{ Lock our data to avoid problems }
HLock(Handle(controlDataHdl));
{ Drawing with the 'cctb' colors requires informations about the foreground color of the control's window, so }
{ store it into our data to avoid another couple of Get/SetGWorld calls later. }
{ Possible improvement: storing this color is needed only if dimFlag = true, so we could calculate dimFlag in this }
{ routine rather than in DrawCelsiusControl }
if BAND(inVarCode, kUseStdColorsVarCodeMask) = 0 then
controlDataHdl^^.fControlOwnerForeColor := saveForeColor;
{ Store into our data the content color of the control's window, because we will need it to frame the }
{ control's bounds when not drawing the inset border }
if GetAuxWin(WindowPtr(controlPort), auxWinHdl) then
;
colorTabHdl := auxWinHdl^^.awCTable;
HLock(Handle(colorTabHdl));
controlDataHdl^^.fControlOwnerContentColor := colorTabHdl^^.ctTable[wContentColor].rgb;
HUnlock(Handle(colorTabHdl));
{ If the offscreen world is available, update it if the depth changed between the last drawing and call }
{ DeviceLoop 1) first to draw into it and 2) to blit from it to the control's port; otherwise call DeviceLoop }
{ to do a simple draw in the control's window }
offscreenDrawFlag := (controlDataHdl^^.fOffscreenWorldPtr <> nil) & LockPixels(GetGWorldPixMap(controlDataHdl^^.fOffscreenWorldPtr));
{ Signal the availability of the offscreen world to the actual draw procedure }
controlDataHdl^^.fOffscreenDrawAvailable := offscreenDrawFlag;
if offscreenDrawFlag then
begin
{ Update our offscreen world if the depth has changed, and store the new depth}
currDepth := GetControlPortDepth(inControlHdl);
if controlDataHdl^^.fDepth <> currDepth then
begin
ignoredFlags := UpdateGWorld(controlDataHdl^^.fOffscreenWorldPtr, kDeepestDeviceDepth, controlDataHdl^^.fOffscreenWorldPtr^.portRect, nil, nil, kOffWorldFlags);
controlDataHdl^^.fDepth := currDepth;
end;
{ Draw the control in the offscreen world }
DeviceLoop(drawingRgn, controlDataHdl^^.fDrawControlUPP, SInt32(inControlHdl), kDeviceLoopFlags);
{ Copy the newly drawn control from the offscreen world to the control's port }
DeviceLoop(drawingRgn, controlDataHdl^^.fBlitControlUPP, SInt32(inControlHdl), kDeviceLoopFlags);
UnlockPixels(GetGWorldPixMap(controlDataHdl^^.fOffscreenWorldPtr));
end
else
{ The offscreen world is not accessible, so we draw in the control's port anyway }
DeviceLoop(drawingRgn, controlDataHdl^^.fDrawControlUPP, SInt32(inControlHdl), kDeviceLoopFlags);
{ Restore the previous settings, unlock our data and exit }
SetClip(saveClip);
if saveClip <> nil then
DisposeRgn(saveClip);
if controlRgn <> nil then
DisposeRgn(controlRgn);
RGBForeColor(saveForeColor);
RGBBackColor(saveBackColor);
SetPenState(savePen);
SetGWorld(savePort, saveDevice);
HUnlock(Handle(controlDataHdl));
end;
{ InitControlData }
{}
{ Initializes the control private data, filling it with the UPPs, the patterns, ecc. }
{}
{ Entry: inControlHdl = handle to current control }
{ inVarCode = variation code of current control }
procedure InitControlData (inControlHdl: ControlHandle;
inVarCode: SInt16);
var
theDataHdl: CelsiusCDEFDataHandle;
drawUPP, blitUPP: DeviceLoopDrawingUPP;
offWorldPtr: GWorldPtr;
err: OSErr;
begin
theDataHdl := nil;
{ Allocate memory for the control's data }
theDataHdl := CelsiusCDEFDataHandle(NewHandleClear(SizeOf(CelsiusCDEFData)));
err := MemError;
{ If the allocation was successful then fill in the fields, else punt }
if (err = noErr) and (theDataHdl <> nil) then
begin
{ Lock the data to avoid problems (the following calls should move memory) }
HLock(Handle(theDataHdl));
{ Store the draw and blit procedures }
drawUPP := NewDeviceLoopDrawingProc(@DrawCelsiusControl);
theDataHdl^^.fDrawControlUPP := drawUPP;
blitUPP := NewDeviceLoopDrawingProc(@BlitCelsiusControl);
theDataHdl^^.fBlitControlUPP := blitUPP;
{ Store the offscreen graphics world }
err := CreateControlOffscreenWorld(inControlHdl, offWorldPtr);
if err = noErr then
theDataHdl^^.fOffscreenWorldPtr := offWorldPtr;
{ Store the patterns; getting them via Resource Mgr. calls helps insulating us from using the evil }
{ QuickDraw globals }
GetIndPattern(theDataHdl^^.fBlackPattern, sysPatListID, kBlackPatternIndex);
GetIndPattern(theDataHdl^^.fWhitePattern, sysPatListID, kWhitePatternIndex);
GetIndPattern(theDataHdl^^.fDitherPattern, sysPatListID, kGrayPatternIndex);
{ Store the colors; note that the foreground and background colors of the control's window are stored }
{ by the BeginDraw procedure }
SetRGBColor(theDataHdl^^.fBlackColor, kBlackColorRGBComp, kBlackColorRGBComp, kBlackColorRGBComp);
SetRGBColor(theDataHdl^^.fWhiteColor, kWhiteColorRGBComp, kWhiteColorRGBComp, kWhiteColorRGBComp);
if BAND(inVarCode, kUseStdColorsVarCodeMask) <> 0 then
begin
SetRGBColor(theDataHdl^^.fDarkGrayColor, kDarkGrayColorRGBComp, kDarkGrayColorRGBComp, kDarkGrayColorRGBComp);
SetRGBColor(theDataHdl^^.fSteelBlueColor, kSteelBlueColorRGComp, kSteelBlueColorRGComp, kSteelBlueColorBComp);
SetRGBColor(theDataHdl^^.fDimGrayColor, kDimGrayColorRGBComp, kDimGrayColorRGBComp, kDimGrayColorRGBComp);
end;
{ This color is calculated only if the variation code specifies that we should draw the inset effect }
if BAND(inVarCode, kInsetBorderVarCodeMask) <> 0 then
SetRGBColor(theDataHdl^^.fChiselGrayColor, kChiselGrayColorRGBComp, kChiselGrayColorRGBComp, kChiselGrayColorRGBComp);
{ Store the variation code of the current control; unfortunately, we don't have any other way of }
{ passing it to the drawing routine }
theDataHdl^^.fVariationCode := inVarCode;
{ Store the depth of the control's port }
theDataHdl^^.fDepth := GetControlPortDepth(inControlHdl);
{ Unlock data }
HUnlock(Handle(theDataHdl));
end;
{ Store the initialized data in the contrlData field; note that even the NIL handle of a unsuccessful }
{ allocation is stored, because is checked by the draw routine that exits if encounters such a handle }
inControlHdl^^.contrlData := Handle(theDataHdl);
end;
{ DisposeControlData }
{}
{ Disposes of all the private data created at initialization time }
{}
{ Entry: inControlHdl = handle to control }
procedure DisposeControlData (inControlHdl: ControlHandle);
var
theDataHdl: CelsiusCDEFDataHandle;
begin
theDataHdl := CelsiusCDEFDataHandle(inControlHdl^^.contrlData);
{ Go ahead only if our data is not NIL }
if theDataHdl <> nil then
begin
{ Dispose of the draw and blit UPPs }
DisposeRoutineDescriptor(theDataHdl^^.fDrawControlUPP);
DisposeRoutineDescriptor(theDataHdl^^.fBlitControlUPP);
{ Dispose of the offscreen graphics world }
if theDataHdl^^.fOffscreenWorldPtr <> nil then
DisposeGWorld(theDataHdl^^.fOffscreenWorldPtr);
{ Finally, dispose of the data itself and set it to NIL to avoid multiple disposal }
DisposeHandle(Handle(theDataHdl));
theDataHdl := nil;
end;
end;
{ Main }
{}
{ Main entry point for the progress bar control definition function. Dispatches the messages to the }
{ appropriate subroutines }
{}
{ Entry: inVarCode = variation of control to handle }
{ inControlHdl = handle to ControlRecord describing the current control }
{ inMessage = identifies the subfunction requested }
{ inParam = variable value, depending on inMessage }
{ Exit: function result = variable value, depending on inMessage }
function Main (inVarCode: SInt16;
inControlHdl: ControlHandle;
inMessage: ControlDefProcMessage;
inParam: SInt32): SInt32;
var
returnValue: SInt32;
ctrlRecState: SignedByte;
begin
{ Don't waste time if we're called with a NIL control handle (this should not happen anyway) }
if inControlHdl = nil then
Exit(Main);
{ Lock down our control for the whole drawing time }
ctrlRecState := HGetState(Handle(inControlHdl));
HLock(Handle(inControlHdl));
{ Return 0 as default from our defproc (we don't have indicators) }
returnValue := 0;
{ Dispatch the current message to the appropriate subroutine }
case inMessage of
{ Draw the control (only if it's visible) }
drawCntl:
if inControlHdl^^.contrlVis <> 0 then
BeginDraw(inControlHdl, inVarCode);
{ Initializes the control's data }
initCntl:
InitControlData(inControlHdl, inVarCode);
{ Disposes of the control's data }
dispCntl:
DisposeControlData(inControlHdl);
{ Return kInCelsiusControlPart if the click is inside the control's rect }
testCntl:
if PtInRect(Point(inParam), inControlHdl^^.contrlRect) then
returnValue := kInCelsiusControlPart;
{ Return the control's rectangle as a region, in 32-bit addressing mode }
calcCntlRgn:
RectRgn(RgnHandle(inParam), inControlHdl^^.contrlRect);
{ Return the control's rectangle as a region, in 24-bit addressing mode; note that IM-Toolbox Essentials }
{ p. 5-112 says that we should clear the high-order bit before calculating the region, but does not specify }
{ that the region handle we return must be confined to the low 3 bytes of inParam }
calcCRgns:
if BAND(inParam, kClearHighByteMask) = 0 then
RectRgn(RgnHandle(StripAddress(inParam)), inControlHdl^^.contrlRect);
otherwise
;
end;
{ Unlock the control and return }
HSetState(Handle(inControlHdl), ctrlRecState);
Main := returnValue;
end;
end.